package com.zhengbing.tj.aspect;

import com.zhengbing.tj.entity.AuditLog;
import com.zhengbing.tj.service.AsyncAuditLogService;
import com.zhengbing.tj.common.AuditLogUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 完整的审计日志切面类
 * 使用AOP拦截所有需要审计的业务方法，实现业务代码和审计日志代码的完全解耦
 *
 * @author zhengbing
 * @date 2024-08-22
 */
@Aspect
@Component
public class FullAuditLogAspect {
    
    @Autowired
    private AsyncAuditLogService asyncAuditLogService;
    
    // 用于存储更新前的实体数据
    private final Map<String, Object> preUpdateDataMap = new ConcurrentHashMap<>();
    
    /**
     * 用户服务的切点
     */
    @Pointcut("execution(* com.zhengbing.tj.service.impl.UserServiceImpl.save(..)) || " +
              "execution(* com.zhengbing.tj.service.impl.UserServiceImpl.updateById(..)) || " +
              "execution(* com.zhengbing.tj.service.impl.UserServiceImpl.removeById(..))")
    public void userServicePointcut() {}
    
    /**
     * 角色服务的切点
     */
    @Pointcut("execution(* com.zhengbing.tj.service.impl.RoleServiceImpl.save(..)) || " +
              "execution(* com.zhengbing.tj.service.impl.RoleServiceImpl.updateById(..)) || " +
              "execution(* com.zhengbing.tj.service.impl.RoleServiceImpl.removeById(..)) || " +
              "execution(* com.zhengbing.tj.service.impl.RoleServiceImpl.saveUserRoles(..))")
    public void roleServicePointcut() {}
    
    /**
     * 权限服务的切点
     */
    @Pointcut("execution(* com.zhengbing.tj.service.impl.PermissionServiceImpl.save(..)) || " +
              "execution(* com.zhengbing.tj.service.impl.PermissionServiceImpl.updateById(..)) || " +
              "execution(* com.zhengbing.tj.service.impl.PermissionServiceImpl.removeById(..)) || " +
              "execution(* com.zhengbing.tj.service.impl.PermissionServiceImpl.saveRolePermissions(..))")
    public void permissionServicePointcut() {}
    
    /**
     * 拦截用户服务方法并记录审计日志
     */
    @Around("userServicePointcut()")
    public Object aroundUserService(ProceedingJoinPoint joinPoint) throws Throwable {
        return aroundService(joinPoint, "user");
    }
    
    /**
     * 拦截角色服务方法并记录审计日志
     */
    @Around("roleServicePointcut()")
    public Object aroundRoleService(ProceedingJoinPoint joinPoint) throws Throwable {
        return aroundService(joinPoint, "role");
    }
    
    /**
     * 拦截权限服务方法并记录审计日志
     */
    @Around("permissionServicePointcut()")
    public Object aroundPermissionService(ProceedingJoinPoint joinPoint) throws Throwable {
        return aroundService(joinPoint, "permission");
    }
    
    /**
     * 通用的服务方法拦截处理
     */
    private Object aroundService(ProceedingJoinPoint joinPoint, String entityType) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        Object target = joinPoint.getTarget();
        
        // 保存前的实体数据
        Object beforeData = null;
        Serializable entityId = null;
        
        // 特殊处理保存用户角色关系的方法
        if ("saveUserRoles".equals(methodName)) {
            return handleSaveUserRoles(joinPoint);
        }
        
        // 特殊处理保存角色权限关系的方法
        if ("saveRolePermissions".equals(methodName)) {
            return handleSaveRolePermissions(joinPoint);
        }
        
        // 处理更新操作，记录更新前的数据
        if ("updateById".equals(methodName) && args != null && args.length > 0) {
            try {
                Object entity = args[0];
                Method getIdMethod = entity.getClass().getMethod("getId");
                entityId = (Serializable) getIdMethod.invoke(entity);
                
                // 获取更新前的实体数据
                Method getByIdMethod = target.getClass().getMethod("getById", Serializable.class);
                beforeData = getByIdMethod.invoke(target, entityId);
            } catch (Exception e) {
                // 记录异常但不影响主业务流程
                e.printStackTrace();
            }
        }
        
        // 处理删除操作，记录删除前的数据
        if ("removeById".equals(methodName) && args != null && args.length > 0 && args[0] instanceof Serializable) {
            try {
                entityId = (Serializable) args[0];
                
                // 获取删除前的实体数据
                Method getByIdMethod = target.getClass().getMethod("getById", Serializable.class);
                beforeData = getByIdMethod.invoke(target, entityId);
            } catch (Exception e) {
                // 记录异常但不影响主业务流程
                e.printStackTrace();
            }
        }
        
        // 执行原方法
        Object result = joinPoint.proceed();
        
        // 根据不同的操作类型记录审计日志
        if (result instanceof Boolean && (Boolean) result) {
            try {
                if ("save".equals(methodName) && args != null && args.length > 0) {
                    // 保存操作
                    Object entity = args[0];
                    Method getIdMethod = entity.getClass().getMethod("getId");
                    entityId = (Serializable) getIdMethod.invoke(entity);
                    
                    AuditLog log = AuditLogUtil.createAuditLog(entityType, Long.valueOf(entityId.toString()),
                            AuditLogUtil.OPERATION_TYPE_INSERT, null, entity);
                    asyncAuditLogService.recordAuditLogAsync(log);
                } else if ("updateById".equals(methodName) && args != null && args.length > 0 && beforeData != null) {
                    // 更新操作
                    AuditLog log = AuditLogUtil.createAuditLog(entityType, Long.valueOf(entityId.toString()),
                            AuditLogUtil.OPERATION_TYPE_UPDATE, beforeData, args[0]);
                    asyncAuditLogService.recordAuditLogAsync(log);
                } else if ("removeById".equals(methodName) && beforeData != null) {
                    // 删除操作
                    AuditLog log = AuditLogUtil.createAuditLog(entityType, Long.valueOf(entityId.toString()),
                            AuditLogUtil.OPERATION_TYPE_DELETE, beforeData, null);
                    asyncAuditLogService.recordAuditLogAsync(log);
                }
            } catch (Exception e) {
                // 记录异常但不影响主业务流程
                e.printStackTrace();
            }
        }
        
        return result;
    }
    
    /**
     * 处理保存用户角色关系的方法
     */
    private Object handleSaveUserRoles(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length >= 2 && args[0] instanceof Long && args[1] instanceof List) {
            Long userId = (Long) args[0];
            List<Long> roleIds = (List<Long>) args[1];
            
            // 获取操作前的角色信息
            List<Long> beforeRoleIds = null;
            try {
                Object target = joinPoint.getTarget();
                Method getRolesByUserIdMethod = target.getClass().getMethod("getRolesByUserId", Long.class);
                beforeRoleIds = (List<Long>) getRolesByUserIdMethod.invoke(target, userId);
            } catch (Exception e) {
                // 记录异常但不影响主业务流程
                e.printStackTrace();
            }
            
            // 执行原方法
            Object result = joinPoint.proceed();
            
            // 如果操作成功，记录审计日志
            if (result instanceof Boolean && (Boolean) result) {
                try {
                    Map<String, Object> beforeData = new HashMap<>();
                    beforeData.put("userId", userId);
                    beforeData.put("roleIds", beforeRoleIds);
                    
                    Map<String, Object> afterData = new HashMap<>();
                    afterData.put("userId", userId);
                    afterData.put("roleIds", roleIds);
                    
                    AuditLog log = AuditLogUtil.createAuditLog("user_role", userId,
                            AuditLogUtil.OPERATION_TYPE_UPDATE, beforeData, afterData);
                    asyncAuditLogService.recordAuditLogAsync(log);
                } catch (Exception e) {
                    // 记录异常但不影响主业务流程
                    e.printStackTrace();
                }
            }
            
            return result;
        }
        
        // 如果参数不符合预期，直接执行原方法
        return joinPoint.proceed();
    }
    
    /**
     * 处理保存角色权限关系的方法
     */
    private Object handleSaveRolePermissions(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length >= 2 && args[0] instanceof Long && args[1] instanceof List) {
            Long roleId = (Long) args[0];
            List<Long> permissionIds = (List<Long>) args[1];
            
            // 获取操作前的权限信息
            List<Long> beforePermissionIds = null;
            try {
                Object target = joinPoint.getTarget();
                Method getPermissionsByRoleIdMethod = target.getClass().getMethod("getPermissionsByRoleId", Long.class);
                beforePermissionIds = (List<Long>) getPermissionsByRoleIdMethod.invoke(target, roleId);
            } catch (Exception e) {
                // 记录异常但不影响主业务流程
                e.printStackTrace();
            }
            
            // 执行原方法
            Object result = joinPoint.proceed();
            
            // 如果操作成功，记录审计日志
            if (result instanceof Boolean && (Boolean) result) {
                try {
                    Map<String, Object> beforeData = new HashMap<>();
                    beforeData.put("roleId", roleId);
                    beforeData.put("permissionIds", beforePermissionIds);
                    
                    Map<String, Object> afterData = new HashMap<>();
                    afterData.put("roleId", roleId);
                    afterData.put("permissionIds", permissionIds);
                    
                    AuditLog log = AuditLogUtil.createAuditLog("role_permission", roleId,
                            AuditLogUtil.OPERATION_TYPE_UPDATE, beforeData, afterData);
                    asyncAuditLogService.recordAuditLogAsync(log);
                } catch (Exception e) {
                    // 记录异常但不影响主业务流程
                    e.printStackTrace();
                }
            }
            
            return result;
        }
        
        // 如果参数不符合预期，直接执行原方法
        return joinPoint.proceed();
    }
}